﻿using Masuit.Tools;
using Microsoft.AspNetCore.Authentication;
using Newtonsoft.Json.Linq;
using System.Net;
using System.Security.Claims;
using System.Text;
using YaseiAkira.AJ.Util;

namespace WebService.Authorize
{
    public class APITokenAuthenticationHandler : IAuthenticationHandler
    {
        private AuthenticationScheme _scheme;
        private HttpContext _context;

        private static string _errMsg;

        private bool _isAjax = false;

        public Task<AuthenticateResult> AuthenticateAsync()
        {

            var request = _context.Request;

            var apiToken = request.Headers["Authorization"].ToString();

            _isAjax = request.IsAJAXOrJsonRequest();

            _errMsg = "未登录或尚未授权";

            if (string.IsNullOrWhiteSpace(apiToken))
            {
                _errMsg = "授权凭证缺失";
                return HandleError(HttpStatusCode.OK);
            }

            var commonUser = Util.DecryptAuthToken(apiToken);

            if (commonUser.Id == 0)
            {
                _errMsg = "授权凭证有误";
                return HandleError(HttpStatusCode.OK);
            }

            if (commonUser.ExpireDate.HasValue && DateTime.Now > commonUser.ExpireDate) 
            {
                _errMsg = "授权凭证已过期";
                return HandleError(HttpStatusCode.Unauthorized);
            }

            // 获取自定义的idtype描述

            var endpoint = _context.GetEndpoint();

            if (endpoint == null)
            {
                _errMsg = "非法访问";
                return HandleError(HttpStatusCode.OK);
            }

            var allowAttr = endpoint.Metadata.GetMetadata<AJAllowIdentityAttribute>();

            if (allowAttr != null && !allowAttr.Type.HasFlag(commonUser.Type))
            {
                return HandleError(HttpStatusCode.Forbidden);
            }

            var success = false;

            IChecker checker = commonUser.GetChecker();

            checker?.ProcessUser(_context, (user) =>
            {
                commonUser = user;
                success = true;
            }, (msg) =>
            {
                _errMsg = msg;
            }, (msg) =>
            {
                _errMsg = msg;
            });

            if (success)
            {
                var claims = commonUser.CreateMainClaims();
                claims.AddRange(commonUser.CreateOtherClaims());

                var idn = new ClaimsIdentity(claims, _scheme.Name);

                var principal = new ClaimsPrincipal(idn);

                var ticket = new AuthenticationTicket(principal, _scheme.Name);

                return Task.FromResult(AuthenticateResult.Success(ticket));
            }

            return HandleError();
        }

        private Task<AuthenticateResult> HandleError(HttpStatusCode httpStatusCode = HttpStatusCode.Unauthorized)
        {
            _context.Response.StatusCode = (int)httpStatusCode;
            return Task.FromResult(AuthenticateResult.Fail(_errMsg));
        }

        public Task ChallengeAsync(AuthenticationProperties properties)
        {
            _context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
            if (_isAjax)
            {
                return _context.Response.WriteAsync(JObject.FromObject(new
                {
                    Success = false,
                    Message = _errMsg,
                    NeedLogin = true,
                    Data = ""
                }).ToString(Newtonsoft.Json.Formatting.Indented), Encoding.UTF8);
            }
            else
            {
                return Task.Run(() => _context.Response.Redirect("/home/error"));
            }

        }

        public Task ForbidAsync(AuthenticationProperties properties)
        {
            _context.Response.StatusCode = (int)HttpStatusCode.Forbidden;
            if (_isAjax)
            {
                return _context.Response.WriteAsync(JObject.FromObject(new
                {
                    Success = false,
                    Message = _errMsg,
                    NeedLogin = true,
                    Data = ""
                }).ToString(Newtonsoft.Json.Formatting.Indented), Encoding.UTF8);
            }
            else
            {
                return Task.Run(() => { _context.Response.Redirect("/home/error"); });
            }

        }

        public Task InitializeAsync(AuthenticationScheme scheme, HttpContext context)
        {
            _scheme = scheme;
            _context = context;

            return Task.CompletedTask;
        }
    }
}
